using System;
using System.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Data.OleDb;
using System.Data.SqlClient;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using System.Text.RegularExpressions;
using System.Timers;
using System.Web;
using System.Web.Mail;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml;

namespace Halide
{
	/// <summary>
	/// The Identify class contains methods and properties for comparing and identifying information.
	/// </summary>
	sealed public class Identify
	{
		private static string _version = "2009.03.01A";

		/// <summary>
		/// Return the version number of the class; read-only.
		/// </summary>
		/// <value>
		/// A string with the version number of the class.
		/// </value>
		public static string Version
		{
			get
			{
				return _version;
			}
		}

		private Identify()
	    {
	    }

		//		_________________________________________________________________________________________________
		//		
		//		REGULAR EXPRESSION CONSTANTS
		//
		//		These are constants that use regular expressions to compare common types of string data,
		//		like email addresses, zip codes, etc.
		//

		/// <summary>
		/// Standard RFC3986 approved email addresses.
		/// </summary>
		/// <example>
		/// Allows: joe@domain.com, joe.smith@domain.com, joe.o'donnel@domain.com
		/// </example>
		public const string REGEX_EMAIL = @"^([a-zA-Z0-9_'+*$%\^&!\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9:]{2,4})+$";

		/// <summary>
		/// File size in KB, MB, GB, TB or PB.
		/// </summary>
		/// <example>
		/// Allows: 1kb, 1 KB, 15.7mb, et al.
		/// </example>
		public const string REGEX_FILE_SIZE = @"^\d{1}[0-9\.]*\s?(kb|Kb|KB|mb|Mb|MB|gb|Gb|GB|tb|Tb|TB|pb|Pb|PB){1}$";

		/// <summary>
		/// Standard 5 digit U.S. zip code.
		/// </summary>
		/// <example>
		/// Allows: 12345
		/// </example>
		public const string REGEX_US_ZIPCODE = @"^\d{5}$" ;

		/// <summary>
		/// Standard 5 digit U.S. zip code with four extra digits.
		/// </summary>
		/// <example>
		/// Allows: 12345-1234
		/// </example>
		public const string REGEX_US_ZIPCODE_PLUS_FOUR = @"^\d{5}((-|\s)?\d{4})$";

		/// <summary>
		/// Standard 5 digit U.S. zip code with optional four extra digits.
		/// </summary>
		/// <example>
		/// Allows: 12345-1234, 12345
		/// </example>
		public const string REGEX_US_ZIPCODE_PLUS_FOUR_OPTIONAL = @"^\d{5}((-|\s)?\d{4})?$" ;

		/// <summary>
		/// Standard 2-letter state abbreviation, case insensitive.
		/// </summary>
		public const string REGEX_US_2_LETTER_STATE = @"^([Aa][LlKkSsZzRrAaEePp]|[Cc][AaOoTt]|[Dd][EeCc]|[Ff][LlMm]|[Gg][AaUu]|[Hh][Ii]|[Ii][AaDdLlNn]|[Kk][SsYy]|[Ll][Aa]|[Mm][AaDdEeHhIiNnOoPpSsTt]|[Nn][CcDdEeHhJjMmVvYy]|[Oo][HhKkRr]|[Pp][AaRrWw]|[Rr][Ii]|[Ss][CcDd]|[Tt][NnXx]|[Uu][Tt]|[Vv][AaIiTt]|[Ww][AaIiVvYy])$";

		/// <summary>
		/// Permissive US Telephone Regex. Does not allow extensions.
		/// </summary>
		/// <example>
		/// Allows: 111-2222, 324-234-3433, 3242343434, (234)234-2343, (234) 234-2343
		/// </example>
		public const string REGEX_US_TELEPHONE = @"^([\(]{1}[0-9]{3}[\)]{1}[\.| |\-]{0,1}|^[0-9]{3}[\.|\-| ]?)?[0-9]{3}(\.|\-| )?[0-9]{4}$";

		/// <summary>
		/// Permissive US Telephone Regex. Does not allow extensions.
		/// </summary>
		/// <example>
		/// Allows: 324-234-3433, 3242343434, (234)234-2343, (234) 234-2343
		/// </example>
		public const string REGEX_US_TELEPHONE_10 = @"^\(\d{3}\) ?\d{3}( |-)?\d{4}|^\d{3}( |-)?\d{3}( |-)?\d{4}";

		/// <summary>
		/// This matches a url in the generic format 
		/// scheme://authority/path?query#fragment
		/// </summary>
		/// <example>
		/// Allows: http://www.yahoo.com, https://www.yahoo.com, ftp://www.yahoo.com
		/// </example>
		public const string REGEX_URL = @"^(?<Protocol>\w+):\/\/(?<Domain>[\w.]+\/?)\S*$";

		/// <summary>
		/// This matches an ip address in the format xxx-xxx-xxx-xxx
		/// each group of xxx must be less than or equal to 255
		/// </summary>
		/// <example>
		/// Allows: 123.123.123.123, 192.168.1.1
		/// </example>
		public const string REGEX_IP_ADDRESS = @"^(?<First>2[0-4]\d|25[0-5]|[01]?\d\d?)\.(?<Second>2[0-4]\d|25[0-5]|[01]?\d\d?)\.(?<Third>2[0-4]\d|25[0-5]|[01]?\d\d?)\.(?<Fourth>2[0-4]\d|25[0-5]|[01]?\d\d?)$";

		/// <summary>
		/// This matches a date in the format mm/dd/yy
		/// </summary>
		/// <example>
		/// Allows: 01/05/05, 12/30/99, 04/11/05
		/// Does not allow: 01/05/2000, 2/2/02
		/// </example> 
		public const string REGEX_DATE_MM_DD_YY = @"^(1[0-2]|0[1-9])/(([1-2][0-9]|3[0-1]|0[1-9])/\d\d)$"; 

	    /// <summary>
	    /// This matches a date in the format m/d/yyyy. Month and day can optionally contain leading zeroes.
	    /// </summary>
	    /// <example>
	    /// Allows: 01/05/2005, 12/30/9999, 4/1/2005
	    /// </example> 
	    public const string REGEX_DATE = @"^((1[0-2]|[1-9]|0[1-9])/([1-2][0-9]|3[0-1]|0[1-9]|[1-9])/(\d{2}|\d{4}))$"; 

		/// <summary>
		/// This matches a date in the format mm/yy
		/// </summary>
		/// <example>
		/// Allows: 01/05, 11/05, 04/99
		/// Does not allow: 1/05, 13/05, 00/05
		/// </example>
		public const string REGEX_DATE_MM_YY = @"^((0[1-9])|(1[0-2]))\/(\d{2})$";

		/// <summary>
		/// 	This matches only numbers(no decimals)
		/// </summary>
		/// <example>
		/// Allows: 0, 1, 123, 4232323, 1212322
		/// </example>
		public const string REGEX_IS_NUMBER_ONLY = @"^([1-9]\d*)$|^0$";

		/// <summary>
		/// This matches any string with only alpha characters upper or lower case(A-Z)
		/// </summary>
		/// <example>
		/// Allows: abc, ABC, abCd, AbCd
		/// Does not allow: A C, abc!, (a,b)
		/// </example>
		public const string REGEX_IS_ALPHA_ONLY = @"^[a-zA-Z]+$"; 

		/// <summary>
		/// This matches any string with only upper case alpha character(A-Z)
		/// </summary>
		public const string REGEX_IS_UPPER_CASE = @"^[A-Z]+$";

		/// <summary>
		/// This matches any string with only lower case alpha character(A-Z)
		/// </summary>
		public const string REGEX_IS_LOWER_CASE = @"^[a-z]+$";

		/// <summary>
		/// Ensures the string only contains alpha-numeric characters, and
		/// not punctuation, spaces, newlines, etc.
		/// </summary>
		/// <example>
		/// Allows: ab2c, 112ABC, ab23Cd
		/// Does not allow: A C, abc!, a.a
		/// </example>
		public const string REGEX_IS_ALPHA_NUMBER_ONLY = @"^[a-zA-Z0-9]+$";

		/// <summary>
		/// Validates US Currency.  Requires $ sign.
		/// Allows for optional commas and decimal. 
		/// No leading zeros. 
		/// </summary>
		/// <example>Allows: $100,000 or $10000.00 or $10.00 or $.10 or $0 or $0.00
		/// Does not allow: $0.10 or 10.00 or 10,000</example>
		public const string REGEX_IS_US_CURRENCY = @"^\$(([1-9]\d*|([1-9]\d{0,2}(\,\d{3})*))(\.\d{1,2})?|(\.\d{1,2}))$|^\$[0](.00)?$";

		/// <summary>
		/// Matches major credit cards including: Visa (length 16, prefix 4);
		/// <para>Mastercard (length 16, prefix 51-55);</para>
		/// <para>Diners Club/Carte Blanche (length 14, prefix 36, 38, or 300-305);</para>
		/// <para>Discover (length 16, prefix 6011);</para>
		/// <para>American Express (length 15, prefix 34 or 37).</para>
		/// <para>Saves the card type as a named group to facilitate further validation 
		/// against a "card type" checkbox in a program.</para>
		/// <para>All 16 digit formats are grouped 4-4-4-4 with an optional hyphen 
		/// or space between each group of 4 digits.</para>
		/// <para>The American Express format is grouped 4-6-5 with an optional hyphen or 
		/// space between each group of digits.</para>
		/// <para>Formatting characters must be consistant, i.e. if two groups are separated by a hyphen, 
		/// all groups must be separated by a hyphen for a match to occur.</para>
		/// </summary>
		public const string REGEX_CREDIT_CARD = @"^(?:(?<Visa>4\d{3})|(?<Mastercard>5[1-5]\d{2})|(?<Discover>6011)|(?<DinersClub>(?:3[68]\d{2})|(?:30[0-5]\d))|(?<AmericanExpress>3[47]\d{2}))([ -]?)(?(DinersClub)(?:\d{6}\1\d{4})|(?(AmericanExpress)(?:\d{6}\1\d{5})|(?:\d{4}\1\d{4}\1\d{4})))$";

		/// <summary>
		/// Matches social security in the following format xxx-xx-xxxx
		/// where x is a number
		/// </summary>
		/// <example>
		/// Allows: 123-45-6789, 232-432-1212
		/// </example>
		public const string REGEX_SOCIAL_SECURITY = @"^\d{3}-\d{2}-\d{4}$";

		/// <summary>
		/// Matches x,x where x is a name, spaces are only allowed between comma and name
		/// </summary>
		/// <example>
		/// Allows: christophersen,eric; christophersen, eric
		/// Not allowed: christophersen ,eric;
		/// </example>
		public const string REGEX_NAME_COMMA_NAME = @"^[a-zA-Z]+,\s?[a-zA-Z]+$";

	    /// <summary>
	    /// Matches time of day. Leading zeroes are ignored.
	    /// </summary>
	    /// <example>
	    /// Allows: 01:15am, 10:15 AM, 2:37, et al.
	    /// </example>
		public const string REGEX_TIME = @"^((0?[1-9]|1[012]):[0-5]\d(\ {0,}[AaPp][Mm]))$";

	    /// <summary>
	    /// Matches time of day in military format.
	    /// </summary>
	    /// <example>
	    /// Allows: 00:15, 23:45, et al.
	    /// </example>
	    public const string REGEX_TIME_24 = @"^((([0][0-2])|([1][0-9])|([2][0-3])):[0-5][0-9])$";

	    /// <summary>
	    /// Matches 3 digits for an area code
	    /// </summary>
	    /// <example>
	    /// Allows: any 3 digits
	    /// </example>
	    public const string REGEX_AREA_CODE = @"^\d\d\d$";

	    /// <summary>
	    /// Matches a domain name.
	    /// </summary>
	    /// <example>
	    /// Allows: site.com, site.com:8080, http://www.site.com, et al.
	    /// </example>
	    public const string REGEX_DOMAIN_NAME = @"^([a-zA-Z\:/]*[a-zA-Z0-9-_]\.[a-zA-Z][a-zA-Z][a-zA-Z0-9\?\=&#_\-/\.\:]*[^\.\s])$";

		//		_________________________________________________________________________________________________
		//		
		//		COMPARISON METHODS
		//
		//		These are methods for performing comparisons and evaluations.
		//
		//

		/// <summary>
		/// Determines if a string value is numeric, but not currency.
		/// </summary>
		/// <example>
		/// <code>
		/// if (Halide.Identify.IsNumeric(value))...
		/// </code>
		/// </example>
		/// <param name="input">The string to process.</param>
		/// <returns>true or false.</returns>
		public static bool IsNumeric(string input)
		{
			Double doubleVal;
			return Double.TryParse(input.Replace("%", ""), (NumberStyles.AllowExponent | NumberStyles.AllowThousands | NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign), null, out doubleVal);
		}

		/// <summary>
		/// Determines if a string value is currency.
		/// </summary>
		/// <example>
		/// <code>
		/// if (Halide.Identify.IsCurrency(value))...
		/// </code>
		/// </example>
		/// <param name="input">The string to process.</param>
		/// <returns>true or false.</returns>
		public static bool IsCurrency(string input)
		{
			Double   doubleVal;
			return Double.TryParse(input, (NumberStyles.AllowCurrencySymbol|NumberStyles.AllowThousands|NumberStyles.AllowDecimalPoint|NumberStyles.AllowLeadingSign), null, out doubleVal);
		}

		/// <summary>
		/// Determines if a string value is numeric, with no symbols, commas, or decimal points.
		/// </summary>
		/// <example>
		/// <code>
		/// if (Halide.Identify.IsPureNumeric(value))...
		/// </code>
		/// </example>
		/// <param name="input">The string to process.</param>
		/// <returns>true or false.</returns>
		public static bool IsPureNumeric(string input)
		{
			Double   doubleVal;
			return Double.TryParse(input, 0, null, out doubleVal);
		}

		/// <summary>
		/// Checks to see if the passed input has the passed pattern
		/// </summary>
		/// <example>
		/// <code>
		/// if (Halide.Identify.HasPattern(REGEX_TIME, "10:00AM"))...
		/// </code>
		/// </example>
		/// <param name="pattern">The pattern to use.</param>
		/// <param name="input">The input to check.</param>
		/// <returns>True if the input has the pattern, false otherwise.</returns>
		public static bool HasPattern( string pattern, string input )
		{
		   Regex regEx = new Regex(pattern);
		   return regEx.IsMatch(input);
		}

		/// <summary>
		/// Checks the passed input to make sure it has all the patterns in the 
		/// passed patterns array.
		/// </summary>
		/// <example>
		/// <code>
		/// string[] patterns = new string[] { REGEX_TIME, REGEX_DATE };
		/// 
		/// if (Halide.Identify.HasPatterns(patterns, "12/20/2007 10:00AM"))...
		/// </code>
		/// </example>
		/// <param name="patterns">Array of patterns.</param>
		/// <param name="input">String value to check.</param>
		/// <returns>True if the input has all of the patterns, false otherwise.</returns>
		public static bool HasPatterns(string[] patterns, string input)
		{
		   for (int i = 0; i < patterns.Length; i++)
		   {
		    if (HasPattern(patterns[i], input) == false)
		        return false;
		   }
		   return true;
		}

		/// <summary>
		/// Determines if a string value is a valid date.
		/// </summary>
		/// <example>
		/// <code>
		/// if (Halide.Identify.IsDate(value))...
		/// </code>
		/// </example>
		/// <param name="input">The string to process.</param>
		/// <returns>true or false.</returns>
		public static bool IsDate(string input)
		{
			if (String.IsNullOrEmpty(input)) return false;

			try
			{
				DateTime dt = Convert.ToDateTime(input);
				return true;
			}
			catch
			{
				return false;
			}
		}

	    /// <summary>
	    /// Uses RegEx to check for password formatting. Alpha-numeric
		/// characters and basic typewriter symbols are allowed.
	    /// </summary>
		/// <example>
		/// <code>
		/// if (Halide.Identify.IsPassword(value, 4, 25)...
		/// </code>
		/// </example>
		/// <param name="inputPassword">Password string to validate.</param>
	    /// <param name="minLen">Minimum length of valid password.</param>
	    /// <param name="maxLen">Maximum length for valid password.</param>
		/// <returns>true if a valid password, false if not.</returns>
		public static bool IsPassword(string inputPassword, int minLen, int maxLen)
		{
			if(Halide.StringJuggler.FixNull(inputPassword) == "" || minLen < 1 || maxLen < 1)
			{
				return (false);
			}
			else
			{
				string strRegex = "^.{" + minLen.ToString() + "," + maxLen.ToString() + "}$";

				Regex re = new Regex(strRegex);

				if (re.IsMatch(inputPassword))
				{
					return (true);
				}
				else
				{
					return (false);
				}
			}
		}

	    /// <summary>
	    /// Uses RegEx to validate the format of an e-mail address.
	    /// </summary>
		/// <example>
		/// <code>
		/// if (Halide.Identify.IsEmail(value)...
		/// </code>
		/// </example>
		/// <param name="inputEmail">E-mail address to validate.</param>
		/// <returns>true if an e-mail address, false if not.</returns>
		public static bool IsEmail(string inputEmail)
		{
			if(Halide.StringJuggler.FixNull(inputEmail).Length < 6)
			{
				return (false);
			}
			else
			{
				// string strRegex =	@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
				string strRegex = REGEX_EMAIL;
			
				Regex re = new Regex(strRegex);

				if (re.IsMatch(inputEmail))
				{
					return (true);
				}
				else
				{
					return (false);
				}
			}
		}

	    /// <summary>
	    /// Determines if a string has one or more domain names in it.
	    /// </summary>
		/// <example>
		/// <code>
		/// if (Halide.Identify.HasDomains(value)...
		/// </code>
		/// </example>
		/// <param name="strVar">String to process.</param>
	    /// <returns>true or false.</returns>
	    public static bool HasDomainNames(string strVar)
	    {
	        return HasPattern( REGEX_DOMAIN_NAME, strVar );
	    }

	    /// <summary>
	    /// Return a comma-separated string with all Halide class versions.
	    /// </summary>
		/// <example>
		/// <code>
		/// Response.Write (Halide.Identify.GetHalideVersions());
		/// </code>
		/// </example>
		/// <returns>Comma-separated string with Halide class versions</returns>
		public static string GetHalideVersions()
		{
			string versions = "";

			versions += "Halide.Compressor Module Version " + Halide.Compressor.Version + ",";
			versions += "Halide.DataRow Module Version " + Halide.DataRow.Version + ",";
			versions += "Halide.Database Module Version " + Halide.Database.Version + ",";
			versions += "Halide.Email Module Version " + Halide.Email.Version + ",";
			versions += "Halide.HtmlForms Module Version " + Halide.HtmlForms.Version + ",";
			versions += "Halide.Http Module Version " + Halide.Http.Version + ",";
			versions += "Halide.Identify Module Version " + Halide.Identify.Version + ",";
			versions += "Halide.ImageJuggler Module Version " + Halide.ImageJuggler.Version + ",";
			versions += "Halide.Secure Module Version " + Halide.Secure.Version + ",";
			versions += "Halide.SmartReader Module Version " + Halide.SmartReader.Version + ",";
			versions += "Halide.StopWatch Module Version " + Halide.StopWatch.Version + ",";
			versions += "Halide.OleDbSmartReader Module Version " + Halide.OleDbSmartReader.Version + ",";
			versions += "Halide.Storage Module Version " + Halide.Storage.Version + ",";
			versions += "Halide.StringJuggler Module Version " + Halide.StringJuggler.Version + ",";
			versions += "Halide.Temporal Module Version " + Halide.Temporal.Version + ",";
			versions += "Halide.XmlJuggler Module Version " + Halide.XmlJuggler.Version;

			return versions;
		}

		// Outer Identify class
	}

	// Outer namespace
}